#pragma once

class Event;

typedef Event* EventFactoryProc();

using namespace Ogre;
//////////////////////////////////////////////////////////////////////////

class EventInfo
{
public:
							EventInfo(const char* name, uint serialID, uint numBytes, 
										EventFactoryProc* factoryProc);

public:
	const char*					GetName() const				{ return m_Name; }
	uint						GetSerialID() const			{ return m_SerialID; }
	uint						GetNumBytes() const			{ return m_NumBytes; }

	Event*						Create() const				
								{ return m_FactoryProc ? m_FactoryProc() : NULL; }

	const char*					GetScriptMethodName() const 
								{ return m_ScriptMethodName ? m_ScriptMethodName : NULL; }

public:
	static const EventInfo*		FindByName(const char* name);
	static const EventInfo*		FindByPtr(const void* ptr);
	static const EventInfo*		FindBySerialID(uint serialID);

	static const uint			GetNumRuntimeIDs();
	static const EventInfo*		GetByRuntimeID(uint runtimeID);

	static void					DestoryStaticData();

	struct LessRuntimeID
	{
		bool operator() (EventInfo* a, EventInfo* b)
		{
			return a->m_RuntimeID < b->m_RuntimeID;
		}
	};

private:
	const char*				CreateScriptMethodName() const;

private:
	const char*				m_Name;
	mutable const char*		m_ScriptMethodName;
	EventInfo*				m_NextReg;
	uint					m_SerialID;
	uint					m_RuntimeID;
	uint					m_NumBytes;
	EventFactoryProc*		m_FactoryProc;

	static uint				g_NumEventInfos;
	static EventInfo**		g_EventInfoArray;
};

//////////////////////////////////////////////////////////////////////////

class IEventHandler
{
public:
	virtual bool			IsEnabled() = 0;
	virtual void			OnEvent(Event* evt) = 0;
};

//////////////////////////////////////////////////////////////////////////

// TODO : if Lua Setting then coding..
//class LuaEventExport;
//class LuaEventImport;

class Event
{
public:
								Event() : Handled(false)	{}
	virtual						~Event()					{}

//	virtual void				Export(LuaEventExport& l);
//	virtual void				Import(LuaEventImport& l);

	virtual const EventInfo*	GetInfo() = 0;

public:
	bool						Handled;
};

//////////////////////////////////////////////////////////////////////////

#define	DECLARE_EVENT(_SERIAL_ID)															\
	enum			{ SERIAL_ID = (_SERIAL_ID) + 0};										\
	public:																					\
	virtual const EventInfo*	GetInfo() { return &g_EventInfo; }							\
	static Event*				__Constructor();											\
	const static EventInfo		g_EventInfo;					

#define IMPLEMENT_EVENT(CLASSNAME)															\
	Event* CLASSNAME::__Constructor() { CLASSNAME* obj = new CLASSNAME(); return obj; }		\
		const EventInfo CLASSNAME::g_EventInfo(												\
		#CLASSNAME, CLASSNAME::SERIAL_ID, sizeof(CLASSNAME), CLASSNAME::__Constructor);

//////////////////////////////////////////////////////////////////////////

#define CAST_EVENT(CLASSNAME, EVT)															\
	( (EVT) && (EVT)->GetInfo() == &CLASSNAME::g_EventInfo ? (CLASSNAME*)(EVT) : NULL)

#define BEGIN_EVENT_HANDLER(EVENT)		{ Event* __evt = (EVENT); const EventInfo* __ei = __evt->GetInfo(); if (0) {
#define ON_EVENT(EVENT_CLASS)			; } else if (__ei == &EVENT_CLASS::g_EventInfo) { EVENT_CLASS* evt = (EVENT_CLASS*)__evt; evt->Handled = true;
#define END_EVENT_HANDLER				; } }

//////////////////////////////////////////////////////////////////////////

typedef uint EventHandlerID;

class EventScheduler;
class EventRecoder;
class EventPlayback;

// TODO: make ref counted (It's confusing that user must not delete EventChannel explicitly)
class EventChannel
{
public:
							EventChannel();
							~EventChannel();

public:
	void					AddHandler(IEventHandler* handler, EventHandlerID serialID = 0);
	EventHandlerID			GetHandlerID(IEventHandler* handler);
	void					RemoveHandler(IEventHandler* handler);

	bool					Contains(IEventHandler* handler);

	uint					GetNumHandlers()			{ return m_HandlerMap.size(); }

public:
	void					Send(IEventHandler* to, Event* evt);
	void					Broadcast(Event* evt);
	void					ScheduleAfter(uint tickAfter, Event* evt, IEventHandler* to = NULL);

	EventScheduler*			GetScheduler()				{ return m_Scheduler; }

public:
	bool					IsSystemChannel()			{ return m_IsSystemChannel; }

private:
	typedef std::map<IEventHandler*, EventHandlerID>	HandlerMap;

	HandlerMap				m_HandlerMap;
	bool					m_IsSystemChannel;

	EventScheduler*			m_Scheduler;
	
	friend struct			BroadcastFunc;
	struct					BroadcastFunc;

	friend class			EventService;
};

//////////////////////////////////////////////////////////////////////////